#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
#include <error.h>
#include <errno.h>

#include "debug.h"
//#include "environ.h"

static unsigned char level_info[9][MAX_LENGTH_OF_LEVEL] = {
	{"none"},
	{"err"},
	{"dbg"},
	{"pri"},
	{"warning"},
	{"main_dbg"},
	{"av_dbg"},
	{"net_dbg"},
	{"t_dbg"},
};

struct log_module log_array[MAX_LOG_MODULE_NUM] = {
	{						//for system debug
		.log_path = LOG_INFO_FILE_PATH"sys_log.txt",
		.max_size = 512*1024,
		.fp = NULL,
		.mutex = PTHREAD_MUTEX_INITIALIZER,
	},
	{						//for protocol debug
		.log_path = LOG_INFO_FILE_PATH"proto_log.txt",
		.max_size = 128*1024,
		.fp = NULL,
		.mutex = PTHREAD_MUTEX_INITIALIZER,
	},
};

#ifdef SAVE_TO_FLASH
static int init_log(struct log_module *plm)
{
	CHECK_ARG(plm);	
	if(access((char *)plm->log_path, R_OK)) {
		plm->fp = fopen((char *)plm->log_path, "w+");
		if(NULL == plm->fp) {
			printf("fopen %s err: %s\n", plm->log_path, strerror(errno));
			return RET_FAILURE;
		}
	} else {
		plm->fp = fopen((char *)plm->log_path, "r+");
		if(NULL == plm->fp) {
			printf("err: fopen %s.\n", plm->log_path);
			return RET_FAILURE;
		}
		fseek(plm->fp, 0, SEEK_END); 
	}
	printf(LIGHT_CYAN"/******* create log file in %s ********/\n"NONE_C, plm->log_path);
	return RET_SUCCESS;
}

static void log_exit(struct log_module *plm)
{
	if(NULL == plm)
		return;
	if(plm->fp) {
		fclose(plm->fp);	
		plm->fp = NULL;
	}
}
#endif


void _write_log_(struct log_module *plm, int _level, 
		const char *fname, const int line, const char *format, ...)
{
	struct tm *ptm;
	time_t times;
	char func[MAX_LENGTH_OF_FUNC];	//函数名最大为12字节，最后一个字节为0
	char date_time[MAX_LENGTH_OF_DATA];	//年月日信息
	char func_info[MAX_LENGTH_OF_FUNC_INFO];//12字节的函数名+16字节的等级信息+其他字符
	char user_info[MAX_CHAR_NUM_IN_LINE - MAX_LENGTH_OF_FUNC_INFO - MAX_LENGTH_OF_DATA];
	//最大的输出信息要减掉日期信息和函数信息
	char pri_data[MAX_CHAR_NUM_IN_LINE];	//一行最大输出信息为1024字节
	va_list args;
	int n, tmp;
	unsigned int level_num = 0;
	if(!(_level & DBG_MASK)) {
		if(_level != ERR && _level != PRI && _level != WRN)
			return;
	}

#ifdef SAVE_TO_FLASH
	long log_size = 0;
	if(access((char *)plm->log_path, R_OK) 
			|| NULL == plm->fp) {
		log_exit(plm);
		init_log(plm);
	}
#endif

	/* get user info*/
	va_start(args, format);
	n = vsnprintf(user_info, sizeof(user_info), format, args);
	if(n < 0) {
		printf("%serr: vsnprintf failed.\n%s", LIGHT_RED, NONE_C);
		return ;
	} else {
		tmp = sizeof(user_info);
		if(n > tmp) {
			user_info[tmp - 4] = '.';
			user_info[tmp - 3] = '.';
			user_info[tmp - 2] = '\n';
			user_info[tmp - 1] = 0;
		}
	}
	va_end(args);

	/* level */
	switch(_level & 0xffffffff) {
		case ERR:
			level_num = 1; break;
		case DBG:
			level_num = 2; break;
		case PRI:
			level_num = 3; break;
		case WRN:
			level_num = 4; break;
		case MAIN_DBG:
			level_num = 5; break;
		case AV_DBG:
			level_num = 6; break;
		case NET_DBG:
			level_num = 7; break;
		case TDBG:
			level_num = 8; break;
		default:
			level_num = 0; break;
	}

	snprintf(func, sizeof(func), "%s", fname);
	tmp = sizeof(func) - 1;
	if(strlen(fname) > tmp) 
		func[tmp - 2] = func[tmp - 1] = '.';
	func[tmp] = 0;
	/* 20预留最大的函数名长度，必须为MAX_LENGTH_OF_FUNC-1大小*/
	snprintf(func_info, sizeof(func_info), "[%-20s %04d] %s: ", func, line, level_info[level_num]);

	/* get cur time */
	struct tm mytm;
	times = time(NULL);
	ptm = localtime_r(&times,&mytm);
	snprintf(date_time, sizeof(date_time), "[%04d-%02d-%02d %02d:%02d:%02d] ", ptm->tm_year+1900,\
			ptm->tm_mon+1, ptm->tm_mday,  ptm->tm_hour, ptm->tm_min, ptm->tm_sec);

	/* get pri data */
	snprintf(pri_data, sizeof(pri_data), "%s %s %s", 
			date_time, func_info, user_info);

	/* send msg to log file */
#ifdef SAVE_TO_FLASH
	pthread_mutex_lock(&plm->mutex);
	if(_level == PRI || _level == ERR || _level == WRN) {
		if(NULL != plm->fp) {
			log_size = ftell(plm->fp);
			if((log_size + strlen(pri_data)) >= plm->max_size) rewind(plm->fp);
			fputs(pri_data, plm->fp);
			fflush(plm->fp);
		} else {
			printf("%s %s%s%s%s", LIGHT_RED, date_time, func_info, pri_data, NONE_C);
			init_log(plm);
		}
	}
	pthread_mutex_unlock(&plm->mutex);
#endif

	/* printf */
	switch(_level & DBG_MASK) {
		case ERR:
			fprintf(stderr, "%s%s%s%s%s", LIGHT_RED, date_time, func_info, user_info, NONE_C);
			break;
		case DBG:
			fprintf(stdout, "%s%s%s%s%s", WHITE, date_time, func_info, user_info, NONE_C);
			break;
		case PRI:
			fprintf(stdout, "%s%s%s%s%s", GREEN, date_time, func_info, user_info, NONE_C);
			break;
		case WRN:
			fprintf(stderr, "%s%s%s%s%s", YELLOW, date_time, func_info, user_info, NONE_C);
			break;
		case MAIN_DBG:
			fprintf(stdout, "%s%s%s%s%s", LIGHT_BLUE, date_time, func_info, user_info, NONE_C);
			break;
		case AV_DBG:
			printf("%s%s%s%s%s", WHITE, date_time, func_info, user_info, NONE_C);
			break;
		case NET_DBG:
			fprintf(stdout, "%s%s%s%s%s", WHITE, date_time, func_info, user_info, NONE_C);
			break;
		case TDBG:
			fprintf(stderr, "%s%s%s%s%s", LIGHT_PURPLE, date_time, func_info, user_info, NONE_C);
			break;
		default:
			break;
	}
}


